/*
 * @Description:
 * @Author: chenjiaxin
 * @Date: 2022-02-02 22:11:18
 * @LastEditTime: 2022-03-09 21:23:48
 * @FilePath: \super-unit\lib\utils\index.js
 */
const { createIterableValue, optoString } = require("../type-tool/index.js");

//深拷贝 对象 数组类型数据的拷贝
const cloneDeepObj = (origin, map = new WeakMap()) => {
  let temp = null;
  if (origin && typeof origin === "object") {
    //object array
    temp = Array.isArray(origin) ? [] : {};

    if (map.has(origin)) {
      return map.get(origin);
    }

    map.set(origin, temp);
    for (const key in origin) {
      temp[key] = cloneDeepObj(origin[key], map);
    }

    // 对Symbol的key进行特殊的处理  for in 是遍历不出Symbol的key的
    const symbolKeys = Object.getOwnPropertySymbols(origin); //得到 对象中 作为 Symbol值 的key
    for (const sKey of symbolKeys) {
      temp[sKey] = cloneDeepObj(origin[sKey], map);
    }
  } else {
    // string undefined boolean number null symbol function bigint
    temp = origin;
  }
  return temp;
};

const cloneDeep = (origin, map = new WeakMap()) => {
  let temp = null; //存储新数据
  if (origin && typeof origin === "object") {
    //Object Array 以及通过new 构造函数都是 object
    //判断是否是 可迭代对象。注：object对象 不是可迭代对象
    let iteratorObj = origin[Symbol.iterator] && origin[Symbol.iterator]();
    if (iteratorObj) {
      // 是可迭代对象 Array  Set  Map
      let origin_types = optoString.call(iteratorObj).replace(/\[|]/g, "").split(" "); // "[object Array Iterator]"
      let valueType = origin_types[1];
      temp = createIterableValue(valueType);

      if (map.has(origin)) {
        return map.get(origin);
      }
      map.set(origin, temp);

      for (const iterator of origin) {
        if (valueType === "Set" || valueType === "Map" || valueType === "WeakSet" || valueType === "WeakMap") {
          temp.add(cloneDeep(iterator, map));
        } else {
          temp.push(cloneDeep(iterator, map));
        }
      }
    } else {
      //不是可迭代对象 这里有object。
      temp = {}; //如果是Proxy对象 把它转成了普通的object对象
      if (map.has(origin)) {
        return map.get(origin);
      }
      map.set(origin, temp);
      for (const key in origin) {
        temp[key] = cloneDeep(origin[key], map);
      }
      // 对Symbol的key进行特殊的处理  for in 是遍历不出Symbol的key的
      let symbolKeys = Object.getOwnPropertySymbols(origin);
      for (const skey of symbolKeys) {
        temp[skey] = cloneDeep(origin[skey], map);
      }
    }
  } else {
    // string undefined boolean number null symbol function  bigint
    temp = origin;
  }
  return temp;
};

/**
 * 两数组交集  [1,3,4,3] [2,3] => [3]
 * @param {*} arr1
 * @param {*} arr2
 */
const intersection = (arr1, arr2) => {
  if (!Array.isArray(arr1) || !Array.isArray(arr2)) {
    throw "arr1 arr2必须是数组";
  }

  //先去重
  let newArr1 = [...new Set(arr1)];

  return newArr1.filter((ele) => arr2.includes(ele));
};
// console.log(intersection([1,3,4, 3], [2, 3]));

// console.log(analyzeUrlQuery("baidu.com?name=cjx&age=18&address=深圳&height=0"));
/**
 * 统计字符串个数
 * @param {String} str "hello world!I love you"
 * @param {Array} args 要去除的字符串
 * @param {String} sort 排序  desc|降序  asc|升序
 */
const stringCount = (str, args, sort = "desc") => {
  let temp = {};
  for (let index = 0; index < str.length; index++) {
    const value = str[index];
    if (args && args.includes(value)) continue;

    if (temp.hasOwnProperty(value)) {
      temp[value]++;
    } else {
      temp[value] = 1;
    }
  }

  let obj = {}; // {2: [a,b], 3: [q,f], ...}
  let keys = []; // 保存键(数字)
  for (const key in temp) {
    const value = temp[key];
    if (obj[value]) {
      obj[value].push(key);
    } else {
      obj[value] = [key];
    }
    if (!keys.includes(value)) {
      keys.push(value);
    }
  }

  let sortKeys = keys.sort((a, b) => (sort == "asc" ? a - b : b - a)); //  排序 [10 , 9, 8, ...]

  let sortObj = {}; // 按value值的 从大到小顺序排序
  for (let index = 0; index < sortKeys.length; index++) {
    const element = sortKeys[index];
    for (const value of obj[element]) {
      sortObj[value] = element;
    }
  }
  return sortObj;
};
// console.log(stringCount("hello world!I love you", [" ", "!"], "asc"));

/**
 * 防抖
 * @param {*} fn 要执行的处理函数
 * @param {*} delay 延迟时间
 * @param {*} immediate 是否立即执行
 * @param {*} callbackFn 回调函数 可以拿到返回值
 * @returns
 */
const debounce = (fn, delay = 500, immediate = false, callbackFn) => {
  let timeId = null;
  let isExecute = false; //是否执行过
  let firstTimer = null;
  function newFn(...args) {
    if (immediate && !isExecute) {
      //===>>>要立即执行
      fn.apply(this, args);
      isExecute = true;
      // 处理 返回值
      if (callbackFn && typeof callbackFn === "function") {
        callbackFn("接收的返回值");
      }
      firstTimer = setTimeout(() => {
        isExecute = false;
      }, delay);
    } else {
      //===>>>不要立即执行
      if (firstTimer) clearTimeout(firstTimer);
      if (timeId) clearTimeout(timeId);
      timeId = setTimeout(() => {
        // fn();
        fn.apply(this, args);
        isExecute = false;
        timeId = null;
        // 处理 返回值
        if (callbackFn && typeof callbackFn === "function") {
          callbackFn("接收的返回值");
        }
      }, delay);
    }
  }
  newFn.cancel = function () {
    if (timeId) {
      clearTimeout(timeId);
      isExecute = false;
      timeId = null;
    }
  };
  return newFn;
};

//节流
/**
 *
 * @param {Function} fn 要触发的函数
 * @param {Number} interval 间隔时间
 * @param {Object} options
 * @returns
 */
const throttle = (fn, interval = 1000, options = { leading: true, traling: false, callbackFn: null }) => {
  let lastTime = 0;
  let { leading, traling, callbackFn } = options;
  let timeId = null;
  function newThrottle(...args) {
    let rowTime = new Date().getTime();
    if (!leading && !lastTime) {
      //不要第一次触发 并且 lastTime 为false 的时候，把 lastTime 赋值 为 当前时间。这样第一次就不会触发了
      lastTime = rowTime;
    }
    // 执行条件 rowTime - lastTime >= interval 就是要去执行 触发函数
    let spaceTime = interval - (rowTime - lastTime);
    if (spaceTime <= 0) {
      //要执行
      // fn();
      fn.apply(this, args);
      lastTime = rowTime;
      if (typeof callbackFn === "function") callbackFn("执行参数");
      return;
    }
    //时间还没有到
    if (traling) {
      if (timeId) clearTimeout(timeId);
      timeId = setTimeout(() => {
        // fn();
        fn.apply(this, args);
        lastTime = new Date().getTime();
        timeId = null;
        if (typeof callbackFn === "function") callbackFn("执行参数");
      }, spaceTime);
    }
  }
  //取消功能
  newThrottle.cancle = function () {
    if (timeId) clearTimeout(timeId);
  };
  return newThrottle;
};

module.exports = {
  cloneDeep,
  intersection,
  stringCount,
  cloneDeepObj,
  debounce,
  throttle
};
